﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace MovieDatabase
{
    public partial class Form1 : Form
    {
        #region Global Variables
        List<moviesFromFile> intialMovieList = new List<moviesFromFile>();
        List<SearchResultByTitle> resultsByTitleList = new List<SearchResultByTitle>();
        List<GridData> GridDataMovieList = new List<GridData>();
        List<string> NotFoundMovies = new List<string>();
        List<string> MovieImageList = new List<string>();
        int totalMovies;
        int moviesnotAllowedInDB = 0;
        const string movieTitleSearchBase = @"http://api.themoviedb.org/3/search/movie?api_key=2c50a994de5291887a4e062edd229a72&query=";
        const string moviePosterBase = @"http://image.tmdb.org/t/p/w780";//plus the path at the end
        const string movieBackDropBase = @"http://image.tmdb.org/t/p/w1280";//plus the path at the end
        const string actorProfileBase = @"http://image.tmdb.org/t/p/w185";
        const string movieIDBase = @"http://api.themoviedb.org/3/movie/";
        const string movieCastAndCrewBase = @"http://api.themoviedb.org/3/movie/";
        const string apiKey = "?api_key=2c50a994de5291887a4e062edd229a72";
        const string backDropSavePathBase = @"C:\Important\Movies\";
        const string posterSavePathBase = @"C:\Important\Movies\";
        DataTable resultsTable = new DataTable();
        BackgroundWorker Carl = new BackgroundWorker();
        #endregion

        public Form1()
        {
            InitializeComponent();
            Carl.DoWork += new DoWorkEventHandler(Carl_DoWork);
            Carl.ProgressChanged += new ProgressChangedEventHandler(Carl_ProgressChanged);
            Carl.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Carl_RunWorkerCompleted);
            Carl.WorkerReportsProgress = true;
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            if (Carl.IsBusy)
            {
            }
            else
            {
                btnStart.Enabled = false;
                dvBasicMovieInfo.DataSource = null;
                resultsTable.Clear();
                NotFoundMovies.Clear();
                lbMoviesNotFound.Items.Clear();
                resultsByTitleList.Clear();
                CreateDataColumns();
                Carl.RunWorkerAsync();
            }
            
        }
        
        public void Carl_DoWork(object sender, DoWorkEventArgs e)
        {
            int moviesNotFound = 0;
            int moviesInfoGrabbed = 0;
            totalMovies = intialMovieList.Count();
            lblMoviesNotFound.Invoke((Action)(() => { lblMoviesNotFound.Visible = true; }));
            lblMovieInfoGrabbed.Invoke((Action)(() => { lblMovieInfoGrabbed.Visible = true; }));
            progBar.Invoke((Action)(() => { progBar.Visible = true; }));

            foreach (var intialmovie in intialMovieList)
            {
                #region Getting Initial Movie Info
                string titleDataGrab = movieTitleSearchBase + intialmovie.Title;
                var request = System.Net.WebRequest.Create(titleDataGrab) as System.Net.HttpWebRequest;
                request.KeepAlive = true;
                request.Method = "GET";
                request.Accept = "application/json";
                request.ContentLength = 0;
                string responseContent = null;
                using (var response = request.GetResponse() as System.Net.HttpWebResponse)
                {
                    using (var reader = new System.IO.StreamReader(response.GetResponseStream()))
                    {
                        responseContent = reader.ReadToEnd();
                    }
                }
                var searchResults = ServiceStack.Text.JsonSerializer.DeserializeFromString<SearchResults>(responseContent);
                var results = searchResults.results ?? new List<SearchResultByTitle>();
                if (results.Count == 0)
                {
                    moviesNotFound++;
                    NotFoundMovies.Add(intialmovie.Title);
                    continue;
                }
                #endregion

                #region Getting info using ID
                //get the id and then send another response using the id
                //needs to go movieIdBase + id + apikey
                SearchResultByTitle responseByTitle = results[0];
                string idDataGrab = movieIDBase + responseByTitle.id + apiKey;
                request = System.Net.WebRequest.Create(idDataGrab) as System.Net.HttpWebRequest;
                responseContent = null;
                using (var responseID = request.GetResponse() as System.Net.HttpWebResponse)
                {
                    using (var reader = new System.IO.StreamReader(responseID.GetResponseStream()))
                    {
                        responseContent = reader.ReadToEnd();
                    }
                }
                SearchResultByID idResults = new SearchResultByID();
                idResults = ServiceStack.Text.JsonSerializer.DeserializeFromString<SearchResultByID>(responseContent);
                resultsTable.Rows.Add(idResults.title, idResults.release_date, idResults.runtime, idResults.budget, idResults.revenue, idResults.id, idResults.imdb_id, idResults.overview, idResults.tagline, idResults.status, idResults.vote_average, idResults.vote_count,idResults.backdrop_Path,idResults.poster_path,idResults.production_companies.FirstOrDefault());
                moviesInfoGrabbed++;
                #endregion

                #region Getting movie poster and backdrop
                if (idResults.poster_path != moviePosterBase && idResults.backdrop_Path != movieBackDropBase && idResults.poster_path != null && idResults.backdrop_Path != null)
                {
                    idResults.poster_path = ImageSave(idResults.title, "Movies", moviePosterBase + idResults.poster_path, "(poster).jpeg");
                    idResults.backdrop_Path = ImageSave(idResults.title, "Movies", movieBackDropBase + idResults.backdrop_Path, "(backdrop).jpeg");
                }
                    MovieImageList.Add(idResults.title);
                #endregion

                #region Getting Cast and Crew
                string castAndCrewDataGrab = movieCastAndCrewBase + idResults.id + "/credits" + apiKey;
                request = System.Net.WebRequest.Create(castAndCrewDataGrab) as System.Net.HttpWebRequest;
                responseContent = null;
                using (var responseCast = request.GetResponse() as System.Net.HttpWebResponse)
                {
                    using (var reader = new System.IO.StreamReader(responseCast.GetResponseStream()))
                    {
                        responseContent = reader.ReadToEnd();
                    }
                }
                var castAndCrewResults = ServiceStack.Text.JsonSerializer.DeserializeFromString<CastAndCrew>(responseContent);
                var cast = castAndCrewResults.Cast;
                #endregion

                using (MovieInfoDBDataContext db = new MovieInfoDBDataContext())
                {
                    if (!Directory.Exists(@"C:\Important\" + idResults.title.Replace("/", "").Replace("\\", "").Replace(":", "").Replace("?", "").Replace("\"", "")))
                    {
                        Directory.CreateDirectory(@"C:\Important\Movies\" + idResults.title.Replace("/", "").Replace("\\", "").Replace(":", "").Replace("?", "").Replace("\"", ""));
                    }
                    Movy movie = new Movy();
                    People person = new People();
                    MovieActor actorLink = new MovieActor();
                    movie.Title = Truncate(idResults.title,100);
                    movie.MovieDbID = (short)idResults.id;
                    movie.Budget = idResults.budget;
                    movie.IMDBID = Truncate(idResults.imdb_id,50);
                    short yearOfRelease = 0;
                    short.TryParse(intialmovie.ReleaseYear,out yearOfRelease);
                    movie.ReleaseYear = yearOfRelease;
                    movie.Revenue = idResults.revenue;
                    movie.Runtime = idResults.runtime;
                    movie.Status = idResults.status;
                    movie.Studio = idResults.production_companies.FirstOrDefault() !=null ? idResults.production_companies[0].name : "";
                    movie.Tagline = idResults.tagline;
                    movie.VoteAverage = idResults.vote_average;
                    movie.VoteCount = idResults.vote_count;
                    movie.Overview = idResults.overview;
                    movie.Genre = idResults.genres.FirstOrDefault() != null ? idResults.genres[0].name : "";
                    movie.Budget = idResults.budget;
                    movie.BackDropPath = Truncate(idResults.backdrop_Path,128);
                    movie.PosterPath = Truncate(idResults.poster_path,128);
                    db.Movies.InsertOnSubmit(movie);
                    foreach (var actor in cast)
                    {
                        var exists = db.Peoples.Count(a => a.TMDBID.Value == actor.id);
                        if (exists > 0)
                        {
                            continue;
                        }
                        string actorDataGrab = @"http://api.themoviedb.org/3/person/" + actor.id + apiKey;
                        request = System.Net.WebRequest.Create(actorDataGrab) as System.Net.HttpWebRequest;
                        responseContent = null;
                        using (var responseInfo = request.GetResponse() as System.Net.HttpWebResponse)
                        {
                            using (var reader = new System.IO.StreamReader(responseInfo.GetResponseStream()))
                            {
                                responseContent = reader.ReadToEnd();
                            }
                        }
                        var actorResults = ServiceStack.Text.JsonSerializer.DeserializeFromString<PersonID>(responseContent);
                        if (!Directory.Exists(@"C:\Important\Actors\" + actorResults.name.Replace("/", "").Replace("\\", "").Replace(":", "").Replace("?", "").Replace("\"", "")))
                        {
                            Directory.CreateDirectory(@"C:\Important\Actors\" + actorResults.name.Replace("/", "").Replace("\\", "").Replace(":", "").Replace("?", "").Replace("\"", ""));
                        }
                        if (actorResults.profile_path != null)
                        {
                            actorResults.profile_path = ImageSave(actorResults.name.Replace("/", "").Replace("\\", "").Replace(":", "").Replace("?", "").Replace("\"", ""), "Actors", actorProfileBase + actorResults.profile_path, "(actor).jpeg");
                        }
                        
                        person.FullName = Truncate(actorResults.name,100);
                        person.BirthDate = actorResults.Birthday;
                        person.DeathDate = actorResults.Deathday;
                        person.Biography = actorResults.Biography;
                        person.PlaceOfBirth = Truncate(actorResults.place_of_birth,100);
                        person.PosterPath = Truncate(actorResults.profile_path,128);
                        person.TMDBID = actor.id;//movie database id
                        db.Peoples.InsertOnSubmit(person);
                        
                        actorLink.Movy = movie;
                        actorLink.People = person;
                        actorLink.CharacterName = Truncate(actor.character,50);
                        db.MovieActors.InsertOnSubmit(actorLink);
                    }
                    //
                    try
                    {
                        db.SubmitChanges();
                    }
                    catch (Exception)
                    {
                        moviesnotAllowedInDB++;
                        writeError(movie,person,actorLink);
                        continue;
                    }
                }//end of database using
                #region not needed
                //adding all the collected info to an object and then adding it to a master list
                /*Movie movieInfo = new Movie();
                movieInfo.Title = idResults.title;
                //movieInfo.searchResultByIDInfo.Add(idResults);//null object ref on this line
                //movieInfo.castAndCrewList.Add(castAndCrewResults);//null object ref on this line
                masterMovieList.Add(movieInfo);*/
                #endregion
                //report progress
                Carl.ReportProgress(moviesnotAllowedInDB, moviesInfoGrabbed);
            }
            dvBasicMovieInfo.Invoke((Action)(() => { dvBasicMovieInfo.DataSource = resultsTable; }));
            dvBasicMovieInfo.Invoke((Action)(() => { dvBasicMovieInfo.Visible = true; }));
            progBar.Invoke((Action)(() => { progBar.Visible = false; }));
        }

        /// <summary>
        /// Uses a web client to download and save off an image.
        /// </summary>
        /// <param name="movieTitle">The title of needed movie</param>
        /// <param name="directory">Which directory the image should go.</param>
        /// <param name="imageRequest_path">The path where the image should be grabbed.</param>
        /// <param name="filetype">The type of image.</param>
        /// <returns></returns>
        public string ImageSave(string movieTitle, string directory,string imageRequest_path, string filetype)
        {//fix this and when a image is saved off just save it as a poster or a backdrop instead of title(backdrop)
            //added in the truncate method
            //need to truncate some jobs and movie properties
            byte[] Imagebytes;
            string imageSavePath = "";
            string replacedTitle = movieTitle.Replace("/", "").Replace("\\", "").Replace(":", "").Replace("?", "").Replace("\"", "");
            #region Requesting and saving the image
            if (imageRequest_path != null)
            {
                using (WebClient wc = new WebClient())
                {
                    Imagebytes = wc.DownloadData(imageRequest_path);
                    imageSavePath = @"C:\Important\"+ directory + "\\" + replacedTitle + "\\";
                    try
                    {
                        System.IO.File.WriteAllBytes(imageSavePath + filetype, Imagebytes);
                    }
                    catch (Exception)
                    {
                        
                        //throw;
                    }
                    
                }
            }
            return imageSavePath + filetype;
            #endregion
        }

        /// <summary>
        /// Writes the current movie,person and actor object when an error is caught.
        /// </summary>
        /// <param name="movie"></param>
        /// <param name="person"></param>
        /// <param name="actor"></param>
        public void writeError(Movy movie, People person, MovieActor actor)
        {
            using (StreamWriter writer = File.AppendText(@"C:\Important\FailedToInsert.txt"))
            {
                writer.WriteLine("Movie Data:\r\n");
                writer.WriteLine(movie.Title);
                writer.WriteLine(movie.ID);
                writer.WriteLine(movie.BackDropPath);
                writer.WriteLine(movie.Budget);
                writer.WriteLine(movie.Genre[0]);
                writer.WriteLine(movie.IMDBID);
                writer.WriteLine(movie.Overview);
                writer.WriteLine(movie.PosterPath);
                writer.WriteLine(movie.ReleaseYear);
                writer.WriteLine(movie.Revenue);
                writer.WriteLine(movie.Runtime);
                writer.WriteLine(movie.Status);
                //writer.WriteLine(movie.Studio[0]);
                writer.WriteLine(movie.Tagline);
                writer.WriteLine(movie.VoteAverage);
                writer.WriteLine(movie.VoteCount);
                writer.WriteLine("Person Data\r\n");
                writer.WriteLine(person.FullName);
                writer.WriteLine(person.Biography);
                writer.WriteLine(person.BirthDate);
                writer.WriteLine(person.DeathDate);
                writer.WriteLine(person.ID);
                writer.WriteLine(person.IMDBID);
                writer.WriteLine(person.PlaceOfBirth);
                writer.WriteLine(person.PosterPath);
                writer.WriteLine(person.TMDBID);
                writer.WriteLine("Actor Data:\r\n");
                writer.WriteLine(actor.CharacterName);
                writer.WriteLine(actor.ID);
                writer.WriteLine(actor.PeopleID);
            }
        }

        public string Truncate(string text, int maxLength)
        {
            // replaces the truncated string to a ...
            const string suffix = "...";
            string truncatedString = text != null ? text.Trim() : "";

            if (maxLength <= 0) return truncatedString;
            int length = maxLength - suffix.Length;

            if (length <= 0) return truncatedString;

            if (text == null || text.Length <= maxLength) return truncatedString;

            truncatedString = text.Substring(0, length);
            truncatedString = truncatedString.TrimEnd();
            truncatedString += suffix;
            return truncatedString;
        }


        public void Carl_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            btnStart.Enabled = true;
            foreach (var item in NotFoundMovies)
            {
                lbMoviesNotFound.Items.Add(item);
            }
            lbMoviesNotFound.Visible = true;
            lblMovieInfoGrabbed.Visible = false;
            MessageBox.Show("Grid has finshed populating", "Task Complete");
        }

        public void Carl_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            lblMovieInfoGrabbed.Text = e.UserState +" / " + totalMovies + " movies have had data grabbed for them!";
            lblMoviesNotFound.Text = e.ProgressPercentage + " movies havent been found in the database!";
        }

        /// <summary>
        /// Creates the needed Datatable components, make sure the table is empty before calling this.
        /// </summary>
        private void CreateDataColumns()
        {
            resultsTable.Columns.Add("Title", typeof(string));
            resultsTable.Columns.Add("Release Date", typeof(string));
            resultsTable.Columns.Add("Runtime", typeof(short));
            resultsTable.Columns.Add("Budget", typeof(int));
            resultsTable.Columns.Add("Revenue", typeof(long));
            resultsTable.Columns.Add("Movie DataBase ID", typeof(int));
            resultsTable.Columns.Add("IMDB ID", typeof(string));
            resultsTable.Columns.Add("Overview", typeof(string));
            resultsTable.Columns.Add("Tagline", typeof(string));
            resultsTable.Columns.Add("Status", typeof(string));
            resultsTable.Columns.Add("Vote Average", typeof(double));
            resultsTable.Columns.Add("Vote Count", typeof(int));
            resultsTable.Columns.Add("Backdrop Path", typeof(string));
            resultsTable.Columns.Add("Poster Path", typeof(string));
            resultsTable.Columns.Add("Studio", typeof(string));
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //pull in the intial list from the file
            using (StreamReader reader = File.OpenText(@"C:\Important\movietitles.txt"))
            {
                while (reader.Peek() > 0)
                {
                    var contents = reader.ReadLine().Split('(');
                    moviesFromFile movie = new moviesFromFile();
                    if (contents.Count() > 2)
                    {
                        movie.Title = contents[1].Replace("(", "").Replace(")", "");
                        movie.ReleaseYear = contents[2].Replace(")", "");
                        if (movie.ReleaseYear == null)
                        {
                            movie.ReleaseYear = "0000";
                        }
                    }
                    else if (contents.Count() == 2)
                    {
                        movie.Title = contents[0].Replace("(", "").Replace(")", "");
                        movie.ReleaseYear = contents[1].Replace(")", "");
                        if (movie.ReleaseYear == null)
                        {
                            movie.ReleaseYear = "0000";
                        }
                    }
                    else
                    {
                        movie.Title = contents[0];
                        movie.ReleaseYear = "0000";
                    }
                    intialMovieList.Add(movie);
                }
            }
            using (MovieInfoDBDataContext db = new MovieInfoDBDataContext())
            {
                var intialQuery = (from a in db.Movies
                          select new GridData
                          {
                              Title = a.Title,
                              ID = a.ID,
                              ReleaseYear = a.ReleaseYear.ToString(),
                              Budget =(int)a.Budget,
                              Revenue = (int)a.Revenue,
                              //finsh adding in the rest of  the properties
                          }).ToList();
                GridDataMovieList = intialQuery;
                dvBasicMovieInfo.DataSource = intialQuery;
                dvBasicMovieInfo.Visible = true;
            }
            //dataGridView1.DataSource = intialMovieList.OrderBy(a => a.Title).ToList();
        }

       /* private void dataGridView1_CellEnter(object sender, DataGridViewCellEventArgs e)
        {

            var gridRow = (GridData)dvBasicMovieInfo.CurrentRow.DataBoundItem;

            try
            {
                if (gridRow.Title != null && pbMoviePoster.Image != Image.FromFile(posterSavePathBase + gridRow.Title + "\\" + gridRow.Title + "(poster).jpeg") && this.BackgroundImage != Image.FromFile(posterSavePathBase + gridRow.Title + "\\" + gridRow.Title + "(backdrop).jpeg"))
                {
                    pbMoviePoster.Image = Image.FromFile(posterSavePathBase + gridRow.Title + "\\" + gridRow.Title + "(poster).jpeg");
                    this.BackgroundImage = Image.FromFile(backDropSavePathBase + gridRow.Title + "\\" + gridRow.Title + "(backdrop).jpeg");
                }
                if (gridRow.ID != null)
                {
                    dvCastInfo.Rows.Clear();
                    using (MovieInfoDBDataContext db = new MovieInfoDBDataContext())
                    {
                        var castQuery = (from a in db.MovieActors
                                   where a.MovieID == gridRow.ID
                                   select new
                                   {
                                       a.CharacterName,
                                       a.People.FullName,
                                       a.People.PosterPath
                                   }).ToList();
                        foreach (var item in castQuery)
                        {
                            try
                            {
                                dvCastInfo.Rows.Add(item.FullName, item.CharacterName,Bitmap.FromFile(item.PosterPath.Replace("\\\\", "\\")));
                            }
                            catch (Exception)
                            {

                                continue;
                            }
                           
                        }
                        dvCastInfo.Visible = true;
                    }//end of using
                }
            }
            catch (Exception x)
            {
                lbMoviesNotFound.Items.Add(x.GetBaseException());
                lbMoviesNotFound.Visible = true;
            }
        }*/
    }
}
